package middlewares

import (
	"errors"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"strings"
	"tem/global"
	"time"
)

type MyClaims struct {
	//除了满足下面的Claims，还需要以下用户信息
	Account string `json:"account"`
	ID      int    `json:"id"`
	//jwt中标准的Claims
	jwt.StandardClaims
}

// 使用指定的 secret 签名声明一个 key ，便于后续获得完整的编码后的字符串token
var key = []byte(global.Config.Jwt.Secret)

func keyFunc(_ *jwt.Token) (i interface{}, err error) {
	return key, nil
}

// GenToken 生成token的方法
func GenToken(account string, id int) (string, error) {
	//创建一个我们自己的声明
	c := MyClaims{
		account, //自定义字段 账号
		id,      //自定义字段 id
		jwt.StandardClaims{
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), //过期时间
			Issuer:    global.Config.Jwt.Issuer,              //签发人
		},
	}

	//使用指定的签名方法创建签名对象
	//这里使用HS256加密算法
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)

	//注意这个地方的 key 一定要是字节切片不能是字符串
	return token.SignedString(key)
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (claims *MyClaims, err error) {
	// 解析token
	var token *jwt.Token
	claims = new(MyClaims)
	token, err = jwt.ParseWithClaims(tokenString, claims, keyFunc)
	if err != nil {
		return nil, err
	}
	// 对token对象中的Claim进行类型断言
	if token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")

}

// JwtParse 中间件
func JwtParse() func(c *gin.Context) {
	return func(c *gin.Context) {
		currentPath := strings.Split(c.Request.URL.Path, "?")[0]
		var flag = true
		for i := range global.WhiteList {
			if currentPath == global.WhiteList[i] {
				flag = false
				break
			}
			flag = true
		}
		if flag {
			authHeader := c.Request.Header.Get("Authorization")
			if authHeader == "" {
				c.JSON(401, gin.H{
					"code": -1,
					"msg":  "未登录",
				})
				c.Abort()
				return
			}
			// 按空格分割
			parts := strings.SplitN(authHeader, " ", 2)
			if !(len(parts) == 2 && parts[0] == "Bearer") {
				c.JSON(401, gin.H{
					"code": -1,
					"msg":  "访问失败,无效的token,请登录!",
				})
				c.Abort()
				return
			}
			// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它
			mc, err := ParseToken(parts[1])
			if err != nil {
				c.JSON(401, gin.H{
					"code": -1,
					"msg":  "访问失败,无效的token,请登录!",
				})
				c.Abort()
				return
			}
			// 将当前请求的userID信息保存到请求的上下文c上
			c.Set("account", mc.Account)
			c.Set("id", mc.ID)
			c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息
		} else {
			c.Next()
		}

	}
}
